#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 压死初学者的三座大山
// -->1.数组数组类型
// -->2.数组数据类型和数组数类型指针
// -->3.数组数据类型和数组数据类型指针的关系

// 四区模型
// 栈是数据结构,特点是先进后出
// 栈区是指的是内存空间的一个区域,是利用栈的特点来进行管理的.在栈区开辟会好一些.
// 栈区stack,由编译器自动分配释放的,存放函数的参数值,局部变量值,分配内存小,速度快
// 堆区 heap,由程序员分配释放,比栈区的速度要慢,不需要调用函数.空间比较大,开辟占用空间比较大的内存结构
// 全局区,静态区static:全局变量和静态变量的存储是放在一块的
// 常量区:字符串常量和其它常量的存储位置
// 程序代码区:存放函数体的二进制代码

// 对称的原则:内存由谁开辟,就有谁释放.生命周期{}
void test1()
{
    int a = 10;// 发现一个int a是个局部练练,就会在栈区开辟4个字节空间.
    // 此过程是操作系统给我们开辟的.
    int b = 20;
    char *str= "123456";// 定义并初始化
    // char *str定义时,在栈区开辟内存,str 变量地址编号在栈区
    // "123456"在文字常量区,放着.把地址给到地址空间里面

}

void test2()
{
    int *a = NULL;
    a = (int *)malloc(sizeof(int));// 在堆区开辟内存
}

char *getStr1()
{
    char *p = "abcd1";
    return p;
}

char *getStr2()
{
    char *p = "abcd2";
    return p;
}

char* getStr3()
{
    char* p = "abcd";
    return p;
}

char* getStr4()
{
    char* p = "abcd";// 存放于全局区
    return p;
}

char * getStr5()
{
    char buf[100];
    memset(buf,0,sizeof(buf));
    strcpy(buf,"abcd1");
    return buf;
}

// 内存四区模型
// stack栈
/*main
   p2 |  0  |
   p1 |  0  |
   a  |  0  |
  getStr1
   p  |  0xf1  |
   p1 |  0  |
   a  |  0  |
*/

// 堆
/*
  0xf1  "abcd1"
*/
// 全局区
// 代码区 -- >

/*
int main()
{
    int a = 0;
    char *p1 = NULL;
    char *p2 = NULL;
    p1 = getStr1();
    p2 =getStr2();
    char * p3 = NULL;
    p3 = (char *)malloc(100);
    return 0
}
*/
//     char *p = "abcd1";// 指针的指向谁,就把谁的地址赋给指针. 指针p指向字符串"abcd1",就把字符串"abcd1"的地址给它

// 程序的内存模型
// 1.一个主程序n个函数组成,c++编译器会建立1个堆区1个栈区.

// 在被调用函数里面malloc的内存,首地址传给别调用哪个函数有两种方法.
// 1.return 2.指针做函数参数
// 主调用函数分配的内存(堆区/栈区/全局区,可以在被调用函数里面使用,(指针做函数参数)
// 在被调用函数分配的内存,malloc内存,static内存可以让主调用函数使用.
// 内存泄漏需要注意.
char *getStr6()
{
    char *p = (char *)malloc(100);
    strcpy(p,"abcd1");// 意思是 把"abcd1" 的首地址的数据拷贝到p指向的内存空间里面
    // 指针变量和它指向的内存空间变量是不同的概念.
    // 没有内存,哪有指针.
    // *((*a + i) + j) = a[i][j];
    // 理解指针,不在于上面一行,不管是在主函数分配的内存,还是在调用函数分配的内存,在栈上分配的内存,在堆上分配的内存.但是临时区属性的内存不可以

    return p;
}

// 建立正确的程序运行内存布局图是学好c的 关键.
// 内存四区模型和函数调用模型,先在一个函数里面玩,再在两个函数里面玩.
// 在函数内的元素
// --> 如数据类型和变量的内存 本质究竟是什么?
// --> 一级指针内存布局图(int *,char *)
// --> 二级指针内存布局图(int **, char **)

// 函数间接
// -->主调用函数分配内存,还是被调用函数分配内存
// -->主调用函数如何使用被调用函数分配的内存(指针做函数参数)



int g_a = 10;

int main()
{
    printf("getstr5 : [%s]\n",getStr5());// 在vs中,Release 可以正常打印,在debug会烫烫烫
    // qt gcc :  getstr5 : [(null)]

#if 0
    int a = 0;
    char* p1 =NULL;
    char* p2 =NULL;
    char* p3 =NULL;
    char* p4 =NULL;
    p1= getStr1();
    p2= getStr2();
    p3= getStr3();
    p4= getStr4();
    printf("p1 : %d,p2 : %d,p3 : %d,p4 : %d\n",p1,p2,p3,p4);
    printf("p1 : %p,p2 : %p,p3 : %p,p4 : %p\n",p1,p2,p3,p4);
    // p3和p4地址是一样的."abcd"返回的地址是一样的.首先分配局部变量指针p,"abcd"是个常量,在常量分配给str地址.之后局部变量释放
    //    qt gcc
    //p1 : 4210788,p2 : 4210794,p3 : 4210800,p4 : 4210800
    //p1 : 00404064,p2 : 0040406A,p3 : 00404070,p4 : 00404070
    // qt中 64位 msvc
    //p1 : 839618560,p2 : 839618568,p3 : 839618576,p4 : 839618584
    //p1 : 00007FF7320B9000,p2 : 00007FF7320B9008,p3 : 00007FF7320B9010,p4 : 00007FF7320B9018
    // vs中:
    //p1 : 410230044,p2 : 410230052,p3 : 410230060,p4 : 410230060
    //p1 : 00007FF618739D1C,p2 : 00007FF618739D24,p3 : 00007FF618739D2C,p4 : 00007FF618739D2C
    // c++编译器优化
    char * p = "abcd";
    char * pp = "abcd";
    if(p == p3 && p == p4 && p == pp)
    {
        printf("p == p3 && p == p4 && pp == p1\n");
    }
#endif
    //    printf("Hello World!\n");
    return 0;
}
